package
{
	import org.flixel.*;
	
	/**
	 * ...
	 * @author JAC
	 */
	public class Maze extends FlxGroup
	{
		private static var _bg:FlxTilemap;
		private static var _map:FlxTilemap;
		private static var _exits:Array;
		
		private static const mapSource:Class = Assets.ImgTileAutoSand;
		private static const bgSource:Class = Assets.ImgTileBG;
		
		public function Maze()
		{
			super();
			
			_bg = new FlxTilemap();
			_bg.solid = false;
			
			_map = new FlxTilemap();
			
			_exits = new Array();
			
			add(_bg);
			add(_map);
			
			load(MazeData.blank);
		}
		
		public function load(maze:Class):void
		{
			var mapData:Array = MazeData.getArray(maze);
			
			var mapString:String = FlxTilemap.arrayToCSV(mapData, widthInTiles);
			_map.loadMap(mapString, mapSource, 0, 0, FlxTilemap.AUTO);
			
			var pathData:Array = new Array();
			var i:uint;
			for (i = 0; i < widthInTiles * heightInTiles; i++)
			{
				pathData.push(0);
			}
			
			var pathString:String = FlxTilemap.arrayToCSV(pathData, widthInTiles);
			_bg.loadMap(pathString, bgSource, 0, 0, FlxTilemap.OFF, 0, 0, 2);
			
			findExits();
		}
		
		public static function get exits():Array
		{
			return _exits;
		}
		
		public static function get tileWidth():uint
		{
			return 25;
		}
		
		public static function get tileHeight():uint
		{
			return 25;
		}
		
		public static function get width():uint
		{
			return 725
		}
		
		public static function get height():uint
		{
			return 525
		}
		
		public static function get widthInTiles():uint
		{
			return 29
		}
		
		public static function get heightInTiles():uint
		{
			return 21
		}
		
		public static function getNearestExit(currentPos:FlxPoint):FlxPoint
		{
			var nearestExit:FlxPoint;
			var minD:Number = 100000;
			var d:Number;
			var i:uint;
			for (i = 0; i < _exits.length; i++)
			{
				d = FlxU.getDistance(currentPos, _exits[i]);
				if (d < minD)
				{
					minD = d;
					nearestExit = _exits[i];
				}
			}
			
			return nearestExit;
		}
		
		public static function getNearestExits(currentPos:FlxPoint):Array
		{
			var i:uint;
			var j:uint;
			
			for (i = 1; i < _exits.length; i++)
			{
				var thisDist:Number = FlxU.getDistance(currentPos, _exits[i]);
				
				for (j = i; j > 0; j--)
				{
					var prevDist:Number = FlxU.getDistance(currentPos, _exits[j - 1]);
					
					if (thisDist >= prevDist)
					{
						break;
					}
					
					var tempExit:FlxPoint = _exits[j];
					_exits[j] = _exits[j - 1];
					_exits[j - 1] = tempExit;
				}
			}
			
			return _exits;
		}
		
		public static function getRandomExit():FlxPoint
		{
			return FlxG.getRandom(_exits) as FlxPoint;
		}
		
		public static function getRandomPathTile(getCenter:Boolean = false):FlxPoint
		{
			return FlxG.getRandom(_map.getTileCoords(0, getCenter)) as FlxPoint;
		}
		
		public static function getTileByPoint(pos:FlxPoint):FlxPoint
		{
			return new FlxPoint(FlxU.floor(pos.x / tileWidth), FlxU.floor(pos.y / tileHeight));
		}
		
		public static function getTileCenterByIndex(i:uint, j:uint):FlxPoint
		{
			return new FlxPoint(i * tileWidth + (tileWidth / 2), j * tileHeight + (tileHeight / 2));
		}
		
		public static function getTileCenterByPoint(point:FlxPoint):FlxPoint
		{
			var tile:FlxPoint = getTileByPoint(point);
			return getTileCenterByIndex(tile.x, tile.y);
		}
		
		public static function findPath(Start:FlxPoint, End:FlxPoint, Simplify:Boolean = true, RaySimplify:Boolean = false):FlxPath
		{
			return _map.findPath(getTileCenterByPoint(Start), getTileCenterByPoint(End), Simplify, RaySimplify);
		}
		
		public static function findPathAvoidingPlayer(Start:FlxPoint, End:FlxPoint, Simplify:Boolean = true, RaySimplify:Boolean = false):FlxPath
		{
			var playerTile:FlxPoint = getTileByPoint(Player.midpoint);
			_map.setTile(playerTile.x, playerTile.y, 1, false);
			
			var path:FlxPath = findPath(Start, End, Simplify, RaySimplify);
			
			_map.setTile(playerTile.x, playerTile.y, 0, false);
			
			return path;
		}
		
		public static function playerIsInSight(pos:FlxPoint, facingDirection:uint, viewDistance:uint):uint
		{
			if (!Player.isAlive)
			{
				return 0;
			}
			return objectIsInSight(pos, Player.player, facingDirection, viewDistance);
		}
		
		public static function objectIsInSight(pos:FlxPoint, other:FlxObject, facingDirection:uint, viewDistance:uint):uint
		{
			var posMidpoint:FlxPoint = getTileCenterByPoint(pos);
			var playerMidpoint:FlxPoint = getTileCenterByPoint(other.getMidpoint());
			var posDifference:FlxPoint = new FlxPoint(posMidpoint.x - playerMidpoint.x, posMidpoint.y - playerMidpoint.y);
			
			if (posDifference.x != 0 && posDifference.y != 0)
			{
				return 0;
			}
			
			switch (facingDirection)
			{
				case FlxObject.NONE: 
				{
					break;
				}
				case FlxObject.UP: 
				{
					if (posDifference.y > 0)
					{
						break;
					}
					return 0;
				}
				case FlxObject.DOWN: 
				{
					if (posDifference.y < 0)
					{
						break;
					}
					return 0;
				}
				case FlxObject.LEFT: 
				{
					if (posDifference.x > 0)
					{
						break;
					}
					return 0;
				}
				case FlxObject.RIGHT: 
				{
					if (posDifference.x < 0)
					{
						break;
					}
					return 0;
				}
				default: 
				{
					break;
				}
			}
			
			var distance:Number = FlxU.abs(posDifference.x) + FlxU.abs(posDifference.y);
			var distanceInTiles:uint = distance / tileWidth;
			if (distanceInTiles > viewDistance)
			{
				return 0;
			}
			
			var xDirection:int = (posDifference.x == 0) ? 0 : posDifference.x / FlxU.abs(posDifference.x) * -1;
			var yDirection:int = (posDifference.y == 0) ? 0 : posDifference.y / FlxU.abs(posDifference.y) * -1;
			var tileIndex:FlxPoint = getTileByPoint(posMidpoint);
			
			var i:uint;
			for (i = 1; i <= distanceInTiles; i++)
			{
				if (_map.getTile(tileIndex.x + (i * xDirection), tileIndex.y + (i * yDirection)) > 0)
				{
					return 0;
				}
			}
			
			return distanceInTiles;
		}
		
		private static function findExits():void
		{
			_exits = null;
			_exits = new Array();
			
			var i:uint;
			for (i = 1; i < widthInTiles - 1; i++)
			{
				if (_map.getTile(i, 0) == 0)
				{
					exits.push(getTileCenterByIndex(i, 0));
					_bg.setTile(i, 0, 1);
				}
				
				if (_map.getTile(i, heightInTiles - 1) == 0)
				{
					exits.push(getTileCenterByIndex(i, heightInTiles - 1));
					_bg.setTile(i, heightInTiles - 1, 1);
				}
			}
			
			for (i = 1; i < heightInTiles - 1; i++)
			{
				if (_map.getTile(0, i) == 0)
				{
					exits.push(getTileCenterByIndex(0, i));
					_bg.setTile(0, i, 1);
				}
				
				if (_map.getTile(widthInTiles - 1, i) == 0)
				{
					exits.push(getTileCenterByIndex(widthInTiles - 1, i));
					_bg.setTile(widthInTiles - 1, i, 1);
				}
			}
		}
		
		private static function index(i:uint, j:uint):uint
		{
			return j * widthInTiles + i;
		}
	}
}